package com.yisu.expressway.network;

import android.text.TextUtils;

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.yisu.expressway.login.LoginManager;
import com.yisu.expressway.utils.HBLog;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
 * Created by will on 15/9/14.
 */
public class ApiModelRequest<T> extends Request<T> {
    private static final String TAG = "ApiModelRequest";

    /** Default charset for request. */
    protected static final String PROTOCOL_CHARSET = "utf-8";

    /** Content type for request. */
    private static final String PROTOCOL_CONTENT_TYPE_JSON =
            String.format("application/json; charset=%s", PROTOCOL_CHARSET);

    private static final String PROTOCOL_CONTENT_TYPE =
            String.format("application/x-www-form-urlencoded; charset=%s", PROTOCOL_CHARSET);

    private static final String PROTOCOL_COOKIE = "Cookie";
    private static final String PROTOCOL_COOKIE_JSESSIONID = "JSESSIONID";
    private static final String PROTOCOL_COOKIE_SERVERID = "SERVERID";

    protected final Response.Listener<T> mListener;
    private String mRequestBody;
    private Map mRequestMapParams;
    private String contentType;

    private static Gson gson = new Gson();
    private static String mCookieSessionId;
    private static String mCookieServerId;

    private TypeToken<T> mResultType;
    protected final ResponseDealListener<T> mResponseDealListener;
    private Map<String, String> customHeaders;

    public ApiModelRequest(String url, TypeToken<T> resultType, Response.Listener<T> listener,
                           Response.ErrorListener errorListener) {
        this(Method.GET, url, resultType, null, null, listener, errorListener);
    }

    public ApiModelRequest(String url, TypeToken<T> resultType, ResponseDealListener<T> responseDealListener, Response.Listener<T> listener,
                           Response.ErrorListener errorListener) {
        this(Method.GET, url, resultType, null, responseDealListener, listener, errorListener);
    }

    public ApiModelRequest(int method, String url, TypeToken<T> resultType, Response.Listener<T> listener,
                           Response.ErrorListener errorListener) {
        this(method, url, resultType, null, null, listener, errorListener);
    }

    public ApiModelRequest(int method, String url, TypeToken<T> resultType, ResponseDealListener<T> responseDealListener, Response.Listener<T> listener,
                           Response.ErrorListener errorListener) {
        this(method, url, resultType, null, responseDealListener, listener, errorListener);
    }

    public ApiModelRequest(int method, String url, TypeToken<T> resultType, Object requestBody, ResponseDealListener<T> responseDealListener, Response.Listener<T> listener,
                           Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        mResultType = resultType;
        mListener = listener;
        mResponseDealListener = responseDealListener;
        if (requestBody != null) {
            contentType = PROTOCOL_CONTENT_TYPE_JSON;
            if (requestBody instanceof JSONObject) {
                mRequestBody = requestBody.toString();
            }else if (requestBody instanceof JSONArray) {
                mRequestBody = requestBody.toString();
            } else if (requestBody instanceof Map) {
                mRequestMapParams = (Map) requestBody;
                contentType = PROTOCOL_CONTENT_TYPE;
            } else if (requestBody instanceof String) {
                mRequestBody = (String) requestBody;
                try {
                    new JSONObject(mRequestBody);
                } catch (JSONException e) {
                    try {
                        new JSONArray(mRequestBody);
                    } catch (JSONException ex1) {
                        // not JSONObject or JSONArray
                        contentType = PROTOCOL_CONTENT_TYPE;
                    }
                }
            }else{
                mRequestBody = requestBody.toString();
                contentType = PROTOCOL_CONTENT_TYPE;
            }
        }else {
            contentType = PROTOCOL_CONTENT_TYPE;
        }
    }

    @Override
    protected void deliverResponse(T response) {
        if(mListener != null) {
            mListener.onResponse(response);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    protected Response<T> parseNetworkResponse(NetworkResponse response){
        if(mResultType == null) {
            return Response.error(new ParseError(new IllegalArgumentException("result type is null")));
        }
        try {
            String respString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
            Class cls = mResultType.getRawType();
            T result;

            if (String.class == cls){
                result = (T)respString;
            }else if(JSONObject.class == cls){
                result = (T)new JSONObject(respString);
            }else if(JSONArray.class == cls){
                result = (T)new JSONArray(respString);
            }else
                result = gson.fromJson(respString, mResultType.getType());

            if (mResponseDealListener != null){
                mResponseDealListener.onResponseDeal(result);
            }
            saveResponseParams(response);
            return Response.success(result,
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JSONException e) {
            return Response.error(new ParseError(e));
        }
    }

    @Override
    public String getBodyContentType() {
        return contentType;
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        try {
            return mRequestBody == null ? super.getBody() : mRequestBody.getBytes(PROTOCOL_CHARSET);
        } catch (UnsupportedEncodingException uee) {
            VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
                    mRequestBody, PROTOCOL_CHARSET);
        }
//        catch (AuthFailureError authFailureError) {
//            authFailureError.printStackTrace();
//        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected Map<String, String> getParams() throws AuthFailureError {
        return mRequestMapParams;
    }

    /**
     * deal response in work thread.
     * @param <T>
     */
    public interface ResponseDealListener<T> {
        void onResponseDeal(T result);
    }



    public void setCustomHeaders(Map<String, String> headers){
        customHeaders = headers;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        if (customHeaders == null) {
            customHeaders = super.getHeaders();
        }

        //插入cookie
        StringBuilder sb = new StringBuilder();
        if (!TextUtils.isEmpty(mCookieSessionId)) {
            sb.append(mCookieSessionId).append(';');
        }
        if (!TextUtils.isEmpty(mCookieServerId)) {
            sb.append(mCookieServerId).append(';');
        }
        if (sb.length() > 0) {
            customHeaders.put(PROTOCOL_COOKIE, sb.toString());
        }
        return customHeaders;

    }



    public void saveResponseParams(NetworkResponse response){
        if (response == null || response.headers == null || response.headers.size() == 0)
            return;
        Map<String, String> headers = response.headers;
        for (String key : response.headers.keySet()) {
            if (HttpHeaderConstants.HEADER_LONG_TOKEN.contentEquals(key)) {
                //save token
                processLongToken(headers.get(HttpHeaderConstants.HEADER_LONG_TOKEN));
            }
//            else if (HttpConsts.HEADER_SET_COOKIE.contentEquals(key)) {
//                //save sessionId serverId
//                String[] cookieStrs = headers.get(HttpConsts.HEADER_SET_COOKIE).split(";");
//                if(cookieStrs.length > 0) {
//                    if (cookieStrs[0].startsWith(PROTOCOL_COOKIE_JSESSIONID)) {
//                        mCookieSessionId = cookieStrs[0];
//                    } else if (cookieStrs[0].startsWith(PROTOCOL_COOKIE_SERVERID)) {
//                        mCookieServerId = cookieStrs[0];
//                    }
//                }
//            }
        }
    }

    private void processLongToken(String token) {
        if (token == null || token.length() == 0)
            return;

        if (!LoginManager.getLongToken().equals(token)) {
            HBLog.i(TAG, "Long token is updated to: " + token);
            LoginManager.setLongToken(token);
        }
    }
}
